<?php
namespace Matrix;

/**
 * 刀锋视图引擎类
 *
 * 提供便捷的PHP写法, 方便美工人员看懂视图文件
 *
 * 启动刀锋视图引擎:
 * <code>
 * Blade::start();
 * </code>
 *
 * 扩展自定义刀锋引擎编译器:
 * <code>
 * Blade::extend('echo', function($value) {
 *	// 处理
 * });
 * </code>
 */
class Blade
{
	/**
	 * 所有刀锋模板引擎编译器
	 *
	 * @var array
	 */
	protected static $compilers = array(
		'extensions',
		'layouts',
		'comments',
		'echos',
		'forelse',
		'empty',
		'endforelse',
		'structure_openings',
		'structure_closings',
		'else',
		'unless',
		'endunless',
		'includes',
		'render_each',
		'render',
		'yields',
		'yield_sections',
		'section_start',
		'section_end',
	);
	
	/**
	 * 所有自定义编译器
	 *
	 * @var array
	 */
	protected static $extensions = array();
	
	/**
	 * 启动刀锋视图模板引擎
	 *
	 * @return	void
	 */
	public static function start()
	{
		Event::listen(View::engine, function($view) {
			if (!str_contains($view->path, BLADE_EXT)) {
				return;
			}
			if (!file_exists($compiled = static::compiled($view->path)) || Blade::expired($view->view, $view->path)) {
				file_put_contents($compiled, Blade::compile($view));
			}
			$view->path = $compiled;
			return $view->get();
		});
	}
	
	/**
	 * 扩展模板引擎编译器
	 *
	 * @param	Closure	$closure
	 * @return	void
	 */
	public static function extend(\Closure $compiler)
	{
		static::$extensions[] = $compiler;
	}
	
	/**
	 * 判断编译后的文件是否已经过期
	 *
	 * @param	string	$view
	 * @param	string	$path
	 * @return	boolean
	 */
	public static function expired($view, $path)
	{
		return filemtime($path) > filemtime(static::compiled($path));
	}
	
	/**
	 * 得到编译后的完整文件路径
	 *
	 * @param	string	$path
	 * @return	string
	 */
	public static function compiled($path)
	{
		return path('storage').'views/'.md5($path);
	}
	
	/**
	 * 编译视图
	 *
	 * @param	View	$view
	 * @return	string
	 */
	public static function compile($view)
	{
		return static::compile_string(file_get_contents($view->path), $view);
	}
	
	/**
	 * 编译字符串
	 *
	 * @param	string	$value
	 * @param	View	$view
	 * @return	string
	 */
	public static function compile_string($value, $view = null)
	{
		foreach (static::$compilers as $compiler) {
			$method = "compile_{$compiler}";
			$value = static::$method($value, $view);
		}
		return $value;
	}
	
	/**
	 * 根据编译函数获取对应的则正表达式
	 *
	 * @param	string	$function
	 * @return	string
	 */
	public static function matcher($function)
	{
		return '/(\s*)@'.$function.'(\s*\(.*\))/';
	}
	
	/**
	 * 重写刀锋`@layout`表达式为有效的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_layouts($value)
	{
		if (!starts_with($value, '@layout')) {
			return $value;
		}
		$lines = preg_split("/(\r?\n)/", $value);
		$pattern = static::matcher('layout');
		$lines[] = preg_replace($pattern, '$1@include$2', $lines[0]);
		return implode(CRLF, array_slice($lines, 1));
	}
	
	/**
	 * 重写刀锋注释为PHP注释
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_comments($value)
	{
		$value = preg_replace('/\{\{--(.+?)(--\}\})?\n/', "<?php // $1 ?>", $value);
		return preg_replace('/\{\{--((.|\s)*?)--\}\}/', "<?php /* $1 */ ?>\n", $value);
	}
	
	/**
	 * 重写刀锋echo为PHP echo
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_echos($value)
	{
		return preg_replace('/\{\{(.+?)\}\}/', '<?php echo $1; ?>', $value);
	}
	
	/**
	 * 重写刀锋`@forelse`为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_forelse($value)
	{
		preg_match_all('/(\s*)@forelse(\s*\(.*\))(\s*)/', $value, $matches);
		foreach ($matches[0] as $forelse) {
			preg_match('/\s*\(\s*(\S*)\s/', $forelse, $variable);
			$if = "<?php if (!empty({$variable[1]})): ?>";
			$search = '/(\s*)@forelse(\s*\(.*\))/';
			$replace = '$1'.$if.'<?php foreach$2: ?>';
			$blade = preg_replace($search, $replace, $forelse);
			
			$value = str_replace($forelse, $blade, $value);
		}
		return $value;
	}
	
	/**
	 * 重写刀锋`@empty`为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_empty($value)
	{
		return str_replace('@empty', '<?php endforeach; ?><?php else: ?>', $value);
	}
	
	/**
	 * 重写刀锋`@endforelase`为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_endforelse($value)
	{
		return str_replace('@endforelse', '<?php endif; ?>', $value);
	}
	
	/**
	 * 重写刀锋开始结构为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_structure_openings($value)
	{
		$pattern = '/(\s*)@(if|elseif|foreach|for|while)(\s*\(.*\))/';
		return preg_replace($pattern, '$1<?php $2$3: ?>', $value);
	}
	
	/**
	 * 重写刀锋结束结构为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_structure_closings($value)
	{
		$pattern = '/(\s*)@(endif|endforeach|endfor|endwhile)(\s*)/';
		return preg_replace($pattern, '$1<?php $2; ?>$3', $value);
	}
	
	/**
	 * 重写刀锋`@else`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_else($value)
	{
		return preg_replace('/(\s*)@(else)(\s*)/', '$1<?php $2: ?>$3', $value);
	}
	
	/**
	 * 重写刀锋`@unless`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_unless($value) {
		$pattern = '/(\s*)@unless(\s*\(.*\))/';
		return preg_replace($pattern, '$1<?php if( ! ($2)): ?>', $value);
	}
	
	/**
	 * 重写刀锋`@endunless`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_endunless($value)
	{
		return str_replace('@endunless', '<?php endif; ?>', $value);
	}
	
	/**
	 * 重写刀锋`@include`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_includes($value)
	{
		$pattern = static::matcher('include');
		return preg_replace($pattern, '$1<?php echo Matrix\\View::make$2->with(get_defined_vars())->render(); ?>', $value);
	}
	
	/**
	 * 重写刀锋`@render`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_render($value)
	{
		$pattern = static::matcher('render');
		return preg_replace($pattern, '$1<?php echo Matrix\\View::make$2->render(); ?>', $value);
	}
	
	/**
	 * 重写刀锋`@render_each`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_render_each($value)
	{
		$pattern = static::matcher('render_each');
		return preg_replace($pattern, '$1<?php echo Matrix\\View::render_each$2; ?>', $value);
	}
	
	/**
	 * 重写刀锋`@section_start`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_section_start($value)
	{
		$pattern = static::matcher('section');
		return preg_replace($pattern, '$1<?php Matrix\\Section::start$2; ?>', $value);
	}
	
	/**
	 * 重写刀锋`@yields`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_yields($value)
	{
		$pattern = static::matcher('yield');
		return preg_replace($pattern, '$1<?php echo Matrix\\Section::yield$2; ?>', $value);
	}
	
	/**
	 * 重写刀锋`@yield_sections`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_yield_sections($value)
	{
		$replace = '<?php echo Matrix\\Section::yield_section(); ?>';
		return str_replace('@yield_section', $replace, $value);
	}
	
	/**
	 * 重写刀锋`@section_end`语法为合法的PHP
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_section_end($value)
	{
		return preg_replace('/@endsection/', '<?php Matrix\\Section::stop(); ?>', $value);
	}
	
	/**
	 * 编译注册的自定义扩展编译器
	 *
	 * @param	string	$value
	 * @return	string
	 */
	protected static function compile_extensions($value)
	{
		foreach (static::$extensions as $compiler) {
			$value = $compiler($value);
		}
		return $value;
	}
}